﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_MEMORY_H
#define _UXS_MEMORY_H

#pragma once

#include "../BaseTypes/Types.h"
#include "../Math/MathFunc.h"

namespace UX_STL
{
namespace UXTypes
{


	//
	// Memory Viewer
	//

	template <typename T, usize I = 400>
	struct TMemoryViewer
	{
		// types
		typedef T					(*p_static_array_t)[I];
		typedef TMemoryViewer<T,I>	Self;

		// value
		p_static_array_t	ptr;

		// cast
		static const Self Cast (const void *ptr)	{ return *PointerCast< Self >( &ptr ); }

		// check
		STATIC_ASSERT( sizeof(T*) == sizeof(p_static_array_t), "size mismatch" );
	};


/*
=================================================
	копирует объект вызывая конструктор копирования
=================================================
*
	template <typename T>
	inline void _construct(T * ptr, const T& e)
	{
		ASSUME( ptr != null );
		new ((void*)ptr) T(e);
	}

/*
=================================================
	вызывает деструктор объекта
=================================================
*
	template <typename T>
	inline void _destruct(T * ptr)
	{
		ASSUME( ptr != null );
		ptr->~T();
	}

/*
=================================================
	выделяет память
=================================================
*/
	inline void * _alloc (usize size) noexcept
	{
		ASSUME( size > 0 );
		void *	ret = ::operator new( size );
		ASSERT( ret != null and "can't allocate memory!" );
		return ret;
	}
	
/*
=================================================
	выделяет память с выравниванием
=================================================
*/
	inline void * _aligned_alloc (usize size, uint align) noexcept
	{
#	ifdef PLATFORM_ANDROID
#		pragma message ("_aligned_alloc changed to _alloc")
		return _alloc( size );
#	else
		ASSUME( size > 0 );
		void *	ret = _hidden_::_mm_malloc( size, align );
		ASSERT( ret != null and "can't allocate aligned memory!" );
		return ret;
#	endif
	}

/*
=================================================
	освобождает память
=================================================
*/
	inline void _dealloc (void *&ptr) noexcept
	{
		::operator delete( ptr );
		ptr = null;
	}
	
/*
=================================================
	освобождает выравненую память
=================================================
*/
	inline void _aligned_dealloc (void *&ptr) noexcept
	{
#	ifdef PLATFORM_ANDROID
#		pragma message ("_aligned_dealloc changed to _dealloc")
		return _dealloc( ptr );
#	else
		_hidden_::_mm_free( ptr );
		ptr = null;
#	endif
	}

/*
=================================================
	создает динамичекий объект
=================================================
*/
	template <typename T>
	inline T & placement_new (void *ptr, const T& value) noexcept
	{
		return *( new(ptr) T(value) );
	}
	
	template <typename T>
	inline T & placement_new (void *ptr, T& value) noexcept
	{
		return *( new(ptr) T(value) );
	}
	
	RVALUE_REFERENCE_ONLY(
		template <typename T>
		inline T & placement_new (void *ptr, T&& value) noexcept
		{
			return *( new(ptr) T(value) );
		}
	)

/*
=================================================
	удаляет динамический объект
=================================================
*/
	template <typename T>
	inline void placement_delete (T &value) noexcept
	{
		value.~T();
		::_hidden_::memset( &value, 0, sizeof(value) );
	}
	
/*
=================================================
	получить смещение (разность) между указателями
=================================================
*/
	template <typename T, typename B>
	inline isize PointerOffset (const T *pleft, const B *pright)
	{
		return ( reinterpret_cast<isize>( pleft ) - reinterpret_cast<isize>( pright ) );
	}
	
/*
=================================================
	побайтовое смещение указателя
=================================================
*/
	namespace _types_hidden_
	{
		template <typename T, typename R>
		struct _PtrMove
		{
		};
		
		template <typename T, typename R>
		struct _PtrMove< T *, R >
		{
			typedef R *	result_t;

			static result_t  Move (T * val, isize offset) {
				return PointerCast< R >( PointerCast< uint8 >( val ) + offset );
			}
		};
		
		template <typename T, typename R>
		struct _PtrMove < const T *, R >
		{
			typedef const R *	result_t;

			static result_t  Move (const T * val, isize offset) {
				return PointerCast< R >( PointerCast< uint8 >( val ) + offset );
			}
		};
	}

	template <typename R, typename T>
	inline typename _types_hidden_::_PtrMove< T, R >::result_t  MovePointer (T ptr, isize offset)
	{
		STATIC_ASSERT( TypeTraits::IsPointer<T>::value, "argument must be pointer" );
		return _types_hidden_::_PtrMove< T, R >::Move( ptr, offset );
	}

	template <typename T>
	inline T  MovePointer (T ptr, isize offset)
	{
		return MovePointer< TypeTraits::RemovePointer< T >::type, T >( ptr, offset );
	}

/*
=================================================
	сравнить указатели
=================================================
*/
	template <typename T0, typename T1>
	inline bool ComparePointers (const T0 p0, const T1 p1)
	{
		return ((const void *) p0) == ((const void *) p1);
	}

/*
=================================================
	возвращает смещение поля класса от начала
	данных класса (от указателя this класса)
=================================================
*
	template <typename C, typename T>
	inline isize ClassFieldOffset (T (C::* field))
	{
		STATIC_ASSERTE( sizeof(field) == sizeof(isize) );
		return reinterpret_cast< const isize &>( field );
	}

	template <typename C0, typename C1, typename T>
	inline isize ClassFieldOffset (C1 (C0::* field), T (C1:: *fieldOfField))
	{
		STATIC_ASSERTE( sizeof(field) == sizeof(isize) and sizeof(fieldOfField) == sizeof(isize) );

		return ClassFieldOffset( field ) + ClassFieldOffset( fieldOfField );
	}
	
/*
=================================================
	возвращает указатель на поле класса, который
	является полем другого класса
=================================================
*
	namespace _types_hidden_
	{
		template <typename C, typename T>
		struct ClassFieldPtr
		{
			typedef T (C:: *type);
		};
	}

	template <typename C0, typename C1, typename T>
	inline typename _types_hidden_::ClassFieldPtr<C0,T>::type
		ClassFieldPointer (C1 (C0::* field), T (C1:: *fieldOfField))
	{
		typedef typename _types_hidden_::ClassFieldPtr<C0,T>::type result_t;

		isize offset = ClassFieldOffset( field, fieldOfField );
		return *reinterpret_cast< result_t *>( &offset );
	}

/*
=================================================
	возвращает смещение между указателями на классы
	(вернет ненулевое значение только при
	 множественном наследовании)
=================================================
*
	template <typename ClassType, typename BaseClass>
	inline isize ClassOffsetOfBase ()
	{
		return PointerOffset( (ClassType *)(BaseClass *)1, (BaseClass *)1 );
	}

/*
=================================================
	стандартные функции копирования памяти
=================================================
*/
	inline void * MemCopy (void *pDst, const void *pSrc, usize uSize)
	{
		return ::_hidden_::memcpy( pDst, pSrc, uSize );
	}

	template <typename T0, typename T1>
	inline void MemCopy (T0 &dst, const T1 &src)
	{
		STATIC_ASSERTE( sizeof(dst) == sizeof(src) );
		MemCopy( &dst, &src, sizeof(dst) );
	}

	inline int MemCopyS (void *pDst, usize uDstSize, const void *pSrc, usize uSrcSize)
	{
		if ( uSrcSize < uDstSize )
			return -1;

		MemCopy( pDst, pSrc, uSrcSize );
		return 0;
	}

	inline void * MemMove (void *pDst, const void *pSrc, usize uSize)
	{
		return ::_hidden_::memmove( pDst, pSrc, uSize );
	}
	/*
	inline void * MemSet (void *pDst, int iValue, usize uSize)
	{
		return ::_hidden_::memset( pDst, iValue, uSize );
	}
	*/
	inline int MemCmp (void *pLeft, void *pRight, usize uSize)
	{
		return ::_hidden_::memcmp( pLeft, pRight, uSize );
	}

/*
=================================================
	сместить даные в 2х мерном буфере
=================================================
*/
	inline bool Memmove2D (void *pMem, const usize uElemSize, usize uWidth, usize uHeight, isize idX, isize idY)
	{
		if ( (usize)UXMath::Abs(idX) >= uWidth or (usize)UXMath::Abs(idY) >= uHeight ) {
			WARNING( "invalid args" );
			return false;
		}

		bool  const		bx		 = idX >= 0;
		bool  const		by		 = idY >= 0;
		usize const		to_x	 = bx ? idX					: 0;
		usize const		to_y	 = by ? uHeight-1			: 0;
		usize const		from_x	 = bx ? 0					: -idX;
		usize const		from_y	 = by ? uHeight - idY - 1	: -idY;
		int   const		step_y	 = by ? -1					: 1;
		usize const		size_x	 = bx ? uWidth - idX		: uWidth + idX;
		usize const		size_y	 = by ? uHeight - idY		: uHeight + idY;

		for (usize i = 0; i < size_y; ++i)
		{
			usize const	o1 = ( to_y   + (i*step_y) )*uWidth + to_x;
			usize const	o2 = ( from_y + (i*step_y) )*uWidth + from_x;

			MemMove( (uint8*)pMem + o1*uElemSize, (uint8*)pMem + o2*uElemSize, size_x );
		}
		return true;
	}

	template <typename T>
	inline bool Memmove2D (T *pMem, usize uWidth, usize uHeight, isize idX, isize idY)
	{
		return Memmove2D( (void*)pMem, sizeof(T), uWidth, uHeight, idX, idY );
	}
	
/*
=================================================
	копировать даные в 2х мерный буфер
=================================================
*/
	inline bool Memcpy2D (void *pDst, const void *pSrc, const usize uElemSize, usize uDstWidth, usize uDstHeight,
						  usize uSrcWidth, usize uSrcHeight, usize uDstOffsetX, usize uDstOffsetY)
	{
		if ( uSrcWidth  + uDstOffsetX > uDstWidth	or
			 uSrcHeight + uDstOffsetY > uDstHeight	or
			 uElemSize == 0 )
		{
			WARNING( "invalid args" );
			return false;
		}

		for (usize i = 0; i < uSrcHeight; ++i)
		{
			usize const	o1 = (uDstOffsetY + i)*uDstWidth + uDstOffsetX;
			usize const	o2 = i*uSrcWidth;

			MemCopy( (uint8*)pDst + o1*uElemSize, (const uint8*)pSrc + o2*uElemSize, uSrcWidth );
		}
		return true;
	}

	template <typename T>
	inline bool Memcpy2D (T *pDst, const T *pSrc, usize uDstWidth, usize uDstHeigth,
						  usize uSrcWidth, usize uSrcHeight, usize uDstOffsetX, usize uDstOffsetY)
	{
		return Memcpy2D( (void*)pDst, (void*)pSrc, sizeof(T), uDstWidth, uDstHeigth,
						 uSrcWidth, uSrcHeight, uDstOffsetX, uDstOffsetY );
	}
	
/*
=================================================
	копировать даные с поворотом 90 (против часовой)
=================================================
*/
	inline void Memcpy2DRotate90rev (void *pDst, const void *pSrc, usize uWidth, usize uHeight, const usize uElemSize)
	{
		for (usize x = 0; x < uWidth;  ++x)
		for (usize y = 0; y < uHeight; ++y)
		{
			const usize	i = y*uWidth + x;
			const usize	j = (uWidth-x-1)*uHeight + y;

			MemCopy( (uint8*)pDst + j*uElemSize, (uint8*)pSrc + i*uElemSize, uElemSize );
		}
	}

	template <typename T>
	inline void Memcpy2DRotate90rev (T *pDst, const T *pSrc, usize uWidth, usize uHeight)
	{
		Memcpy2DRotate90rev( pDst, pSrc, uWidth, uHeight, sizeof(T) );
	}
	
/*
=================================================
	копировать даные с поворотом 90 (по часовой)
=================================================
*/
	inline void Memcpy2DRotate90 (void *pDst, const void *pSrc, usize uWidth, usize uHeight, const usize uElemSize)
	{
		for (usize x = 0; x < uWidth;  ++x)
		for (usize y = 0; y < uHeight; ++y)
		{
			const usize		i = y*uWidth + x;
			const usize		j = (uHeight-y-1)*uHeight + x;
		
			MemCopy( (uint8*)pDst + j*uElemSize, (uint8*)pSrc + i*uElemSize, uElemSize );
		}
	}

	template <typename T>
	inline void Memcpy2DRotate90 (T *pDst, const T *pSrc, usize uWidth, usize uHeight)
	{
		Memcpy2DRotate90( pDst, pSrc, uWidth, uHeight, sizeof(T) );
	}
	
/*
=================================================
	копировать даные с поворотом 180
=================================================
*/
	inline void Memcpy2DRotate180 (void *pDst, const void *pSrc, usize uWidth, usize uHeight, const usize uElemSize)
	{
		for (usize x = 0; x < uWidth;  ++x)
		for (usize y = 0; y < uHeight; ++y)
		{
			const usize	i = y*uWidth + x;
			const usize	j = (uHeight-y-1)*uWidth + x;

			MemCopy( (uint8*)pDst + j*uElemSize, (uint8*)pSrc + i*uElemSize, uElemSize );
		}
	}

	template <typename T>
	inline void Memcpy2DRotate180 (T *pDst, const T *pSrc, usize uWidth, usize uHeight)
	{
		Memcpy2DRotate180( pDst, pSrc, uWidth, uHeight, sizeof(T) );
	}

/*
=================================================
	заполняет массив нулями
=================================================
*/
	template <typename T, usize I>
	inline T * ZeroMem (T (&tArr)[I])
	{
		return (T *)::_hidden_::memset( tArr, 0, sizeof(tArr) );
	}

	template <typename T>
	inline T & ZeroMem (T &val)
	{
		return *(T *)::_hidden_::memset( (void *)&val, 0, sizeof(val) );
	}

	inline void * ZeroMem(void *pDst, usize uSize)
	{
		return ::_hidden_::memset( pDst, 0, uSize );
	}

/*
=================================================
	заполняет массив одинаковыми значениями
=================================================
*/
	template <typename T, typename B>
	inline T * MemSet (T *pBase, usize uCount, const B &tValue)
	{
		for (usize i = 0; i < uCount; ++i) {
			pBase[i] = (T)tValue;
		}

		return pBase;
	}

	template <typename T, typename B>
	inline T * MemSet (T *pBase, T *pEnd, const B &tValue)
	{
		T	*ptr = pBase;

		while( ptr < pEnd )
		{
			*ptr = (T)tValue;
			++ptr;
		}
		return pBase;
	}

	template <typename T, typename B, usize I>
	inline T * MemSet (T (&tArr)[I], const B &tValue)
	{
		return MemSet( tArr, I, tValue );
	}
	
/*
=================================================
	заполняет массив значениями по умолчанию
=================================================
*/
	template <typename T>
	inline T * MemSetDefault (T *pBase, usize uCount)
	{
		return MemSet( pBase, uCount, T() );
	}
	
	template <typename T>
	inline T * MemSetDefault (T *pBase, T *pEnd)
	{
		return MemSet( pBase, pEnd, T() );
	}

	template <typename T, usize I>
	inline T * MemSetDefault (T (&tArr)[I])
	{
		return MemSet( tArr, I, T() );
	}

/*
=================================================
	заполняет 2D область одинаковыми значениями
=================================================
*/
	template <typename T>
	inline bool Memset2D (T *pBase, usize uWidth, usize uHeight, usize uOffsetX, usize uOffsetY,
						  usize uSizeX, usize uSizeY, const T &tValue)
	{
		if ( uOffsetX + uSizeX > uWidth or
			 uOffsetY + uSizeY > uHeight )
		{
			WARNING( "invalid args" );
			return false;
		}

		uSizeX += uOffsetX;
		uSizeY += uOffsetY;

		for (usize x = uOffsetX; x < uSizeX; ++x)
		for (usize y = uOffsetY; y < uSizeY; ++y)
		{
			const usize	i = y*uWidth + x;
			pBase[i] = tValue;
		}
		return true;
	}

	template <typename T>
	inline void Memset2D (T *pBase, usize uWidth, usize uHeight, const T &tValue)
	{
		Memset2D( pBase, uWidth, uHeight, 0, 0, uWidth, uHeight, tValue );
	}

//-------------------------------------------------------------------

}	// UXTypes
}	// UX_STL

#endif	// _UXS_MEMORY_H